/**
 *
 * Copyright 2009 Marco Speranza <marco.speranza79@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.commons.database.jconnectionpool;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.sql.ConnectionPoolDataSource;

/**
 * Classe per la creazione di uno specifico
 * <code>ConnectionPoolDataSource</code> <li>
 * DefaultConnectionPoolDataSource.ORACLE_POOL_CLASS_NAME crea e inizializza la
 * classe <code> oracle.jdbc.pool.OracleConnectionPoolDataSource </code>
 * 
 * <li>DefaultConnectionPoolDataSource.MYSQL_POOL_CLASS_NAME crea e inizializza
 * la classe
 * <code> com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource </code>
 * 
 * <li>se si vuole iniziazzare una classe diversa, usare il costruttore
 * DefaultConnectionPoolDataSource(String className).
 * 
 * @author <a href="mailto:msperanza@users.sourceforge.net">Marco Speranza</a>
 * @version $Id: DataBaseManager.java,v 1.1 2006/08/10 09:48:09 msperanza Exp $
 * 
 */
public class DefaultConnectionPoolDataSource {

    static final String ORACLE_POOL_CLASS_NAME = "oracle.jdbc.pool.OracleConnectionPoolDataSource";

    static final String MYSQL_POOL_CLASS_NAME = "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource";

    static final String ERROR_MSG_INIT_ERROR = "Error while initializing ConnectionPoolDataSource";

    ConnectionPoolDataSource connectionPoolDataSource = null;

    private final String className;

    /**
     * Costruttore. instanza la classe OracleConnectionPoolDataSource
     * 
     */
    public DefaultConnectionPoolDataSource() {
	this(ORACLE_POOL_CLASS_NAME);
    }

    /**
     * Costruttore.
     * 
     * @param className
     *            il nome della classe da instanziare (
     *            DefaultConnectionPoolDataSource.ORACLE_POOL_CLASS_NAME,
     *            DefaultConnectionPoolDataSource.MYSQL_POOL_CLASS_NAME ) oppure
     *            una stringa che rappreenta la classe da instanziare.
     */
    public DefaultConnectionPoolDataSource(String className) {
	this.className = className;
    }

    /**
     * Returns OracleConnectionPoolDataSource
     * 
     * @return ConnectionPoolDataSource
     */

    public ConnectionPoolDataSource getConnectionPoolDataSource() {
	return connectionPoolDataSource;
    }

    /**
     * Inizializza la classe .
     * 
     * @param connString
     *            jdbc url
     * @param username
     *            database username
     * @param password
     *            user password
     */

    public void init(String connString, String username, String password)
	    throws ClassNotFoundException, Exception {
	try {
	    connectionPoolDataSource = (ConnectionPoolDataSource) Class
		    .forName(className).newInstance();
	    if (connString != null) {
		invokeSetMethodCaseInsensitive(connectionPoolDataSource, "url",
			connString);
	    }
	    if (username != null) {
		invokeSetMethodCaseInsensitive(connectionPoolDataSource,
			"user", username);
	    }
	    if (password != null) {
		invokeSetMethodCaseInsensitive(connectionPoolDataSource,
			"password", password);
	    }
	} catch (RuntimeException e) {
	    throw new Exception(ERROR_MSG_INIT_ERROR, e);
	}
    }

    protected void invokeSetMethodCaseInsensitive(Object obj, String prop,
	    String value) throws NoSuchMethodException,
	    InvocationTargetException, IllegalAccessException {
	String alternateMethodName = null;
	Class cl = obj.getClass();
	String setMeth = "set" + prop;
	Method[] methodsList = cl.getMethods();
	boolean methodFound = false;
	int i = 0;

	for (i = 0; i < methodsList.length; ++i) {
	    if (methodsList[i].getName().equalsIgnoreCase(setMeth) == true) {
		Class[] parameterTypes = methodsList[i].getParameterTypes();
		if (parameterTypes.length == 1) {
		    if (parameterTypes[0].getName().equals("java.lang.String")) {
			methodFound = true;
			break;
		    } else {
			alternateMethodName = methodsList[i].getName();
		    }
		}
	    }
	}

	if (methodFound == true) {
	    Object[] params = { value };
	    methodsList[i].invoke(obj, params);
	    return;
	}
	if (alternateMethodName != null) {
	    try {
		Class[] cldef = { Integer.TYPE };
		Method meth = cl.getMethod(alternateMethodName, cldef);
		Object[] params = { Integer.valueOf(value) };
		meth.invoke(obj, params);
		return;
	    } catch (NoSuchMethodException nsmex) {
		Class[] cldef = { Boolean.TYPE };
		Method meth = cl.getMethod(alternateMethodName, cldef);
		Object[] params = { Boolean.valueOf(value) };
		meth.invoke(obj, params);
		return;
	    }
	} else {
	    throw new NoSuchMethodException(setMeth);
	}
    }

}